﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using FTD2XX_NET;

namespace MicroLESA
{
    public partial class MicroLESAForm : Form
    {
        public MicroLESAForm()
        {
            InitializeComponent();
            RefreshMarkers();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            tChart1.Series[0].Clear();
            double[] x = new double[1000000], y = new double[1000000];
            for (int i = 0; i < 10000; i++)
            {
                x[i] = i;
                y[i] = x[i] * x[i];
            tChart1.Series[0].Add(x[i], y[i]);
            }               

        }


        private void panel1_Paint(object sender, PaintEventArgs e)
        {

        }

        private void tChart1_AfterDraw(object sender, Steema.TeeChart.Drawing.Graphics3D g)
        {

        }

        public Bitmap LesaBitmap = null;
        unsafe private void RefreshPanel()
        {
            //Stopwatch s = new Stopwatch();
            //s.Start();
            try
            {
                if (LesaBitmap == null)
                    LesaBitmap = new Bitmap(tChart1.Width, tChart1.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                if (LesaBitmap.Width != tChart1.Width || LesaBitmap.Height != tChart1.Height)
                    LesaBitmap = new Bitmap(tChart1.Width, tChart1.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                tChart1.DrawToBitmap(LesaBitmap, new Rectangle(0, 0, tChart1.Width, tChart1.Height));
                BitmapData bd = LesaBitmap.LockBits(new Rectangle(0, 0, tChart1.Width, tChart1.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
                int* a = (int *)(bd.Scan0.ToPointer());
                int N = tChart1.Width * tChart1.Height;
                int ColorControl = SystemColors.Control.ToArgb();
                for (int i = 0; i < N; i++)
                    if (a[i] == ColorControl)
                        a[i] = int.MaxValue;

                LesaBitmap.UnlockBits(bd);

                //panel1.BackgroundImage = LesaBitmap;
            }
            catch { }
            //Text = s.ElapsedMilliseconds.ToString();
        }

        private void tChart1_Paint(object sender, PaintEventArgs e)
        {
            RefreshPanel();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            RefreshPanel();
        }


        //ftdi support

        private void WriteInfo(string info)
        {
            infoTextBox.Text += info + "\r\n";
            infoTextBox.ScrollToCaret();
        }

        private bool initialization()
        {
            uint ftdiDeviceCount = 0;

            // Create new instance of the FTDI device class
            myFtdiDevice = new FTDI();

            // Determine the number of FTDI devices connected to the machine
            ftStatus = myFtdiDevice.GetNumberOfDevices(ref ftdiDeviceCount);
            // Check status
            if (ftStatus == FTDI.FT_STATUS.FT_OK)
            {
                WriteInfo("Количество FTDI устройств: " + ftdiDeviceCount.ToString());
            }
            else
            {
                WriteInfo("Не удалось определить количество подключенных устройств (Ошибка " + ftStatus.ToString() + ")");

                return (false);
            }

            // If no devices available, return
            if (ftdiDeviceCount == 0)
            {
                WriteInfo("Не обнаружено FTDI устройств!");

                return (false);
            }

            // Allocate storage for device info list

            FTDI.FT_DEVICE_INFO_NODE[] ftdiDeviceList = new FTDI.FT_DEVICE_INFO_NODE[ftdiDeviceCount];

            // Populate our device list
            ftStatus = myFtdiDevice.GetDeviceList(ftdiDeviceList);

            if (ftStatus == FTDI.FT_STATUS.FT_OK)
            {
                uint i = 0;
                WriteInfo("Device Index: " + i.ToString());
                WriteInfo("Flags: " + String.Format("{0:x}", ftdiDeviceList[i].Flags));
                WriteInfo("Type: " + ftdiDeviceList[i].Type.ToString());
                WriteInfo("ID: " + String.Format("{0:x}", ftdiDeviceList[i].ID));
                WriteInfo("Location ID: " + String.Format("{0:x}", ftdiDeviceList[i].LocId));
                WriteInfo("Serial Number: " + ftdiDeviceList[i].SerialNumber.ToString());
                WriteInfo("Description: " + ftdiDeviceList[i].Description.ToString());
                WriteInfo("");
            }


            // Open first device in our list by serial number
            ftStatus = myFtdiDevice.OpenBySerialNumber(ftdiDeviceList[0].SerialNumber);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
            {
                WriteInfo("Не удалось получить доступ к устройству! (Ошибка " + ftStatus.ToString() + ")");

                return (false);
            }


            // Set up device data parameters
            // Set Baud rate to 921600
            ftStatus = myFtdiDevice.SetBaudRate(1200);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
            {
                WriteInfo("Не удалось установить скорость передачи данных (Ошибка " + ftStatus.ToString() + ")");

                return (false);
            }

            // Set data characteristics - Data bits, Stop bits, Parity
            ftStatus = myFtdiDevice.SetDataCharacteristics(FTDI.FT_DATA_BITS.FT_BITS_8, FTDI.FT_STOP_BITS.FT_STOP_BITS_1, FTDI.FT_PARITY.FT_PARITY_NONE);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
            {
                WriteInfo("Не удалось установить параметры передачи данных (Ошибка " + ftStatus.ToString() + ")");

                return (false);
            }

            // Set flow control - set RTS/CTS flow control
            ftStatus = myFtdiDevice.SetFlowControl(FTDI.FT_FLOW_CONTROL.FT_FLOW_NONE, 0x17, 0x19);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
            {
                WriteInfo("Не удалось установить параметры передачи данных (Ошибка " + ftStatus.ToString() + ")");

                return (false);
            }

            // Set read and write timeout to 1 seconds
            ftStatus = myFtdiDevice.SetTimeouts(1000, 1000);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
            {
                WriteInfo("Не удалось установить таймауты записи и чтения! (Ошибка " + ftStatus.ToString() + ")");

                return (false);
            }

            // Working with device
            return SetDeviceParams();


        }

        private bool SetDeviceParams()
        {
            byte[] btimer = expos(IntTime);
            byte[] bframes = BitConverter.GetBytes(frames);
            byte[] dataBuffer = new byte[12] { 1, 1, 2, 3, 100, 1, btimer[0], btimer[1], btimer[2], btimer[3], bframes[0], bframes[1] };
            uint bytesWritten = 0;
            ftStatus = myFtdiDevice.Write(dataBuffer, dataBuffer.Length, ref bytesWritten);
            FramesAfterParamsSet = 0;
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
            {
                WriteInfo("Не удалось записать данные на устройство! (Ошибка " + ftStatus.ToString() + ")");

                return (false);
            }
            else
                return true;
        }


        private void closedevice()
        {
            try
            {
                ftStatus = myFtdiDevice.Close();
            }
            catch
            {
                MessageBox.Show("Случился ППЦ");
            }

            if (ftStatus != FTDI.FT_STATUS.FT_OK)
            {
                WriteInfo("Не удалось отключиться от устройства! (Ошибка " + ftStatus.ToString() + ")");
                return;
            }
            else WriteInfo("Устройство отключено.");
        }


        private byte[] expos(int exposition)
        {
            byte[] temp = BitConverter.GetBytes(exposition * 750 / 50);
            byte[] btimer = new byte[4] { 50, 0, temp[0], temp[1] };
            return (btimer);
        }
        private double[] transferData()
        {

            byte[] dataBuffer = new byte[1] { 2 };
            uint bytesWritten = 0;
            ftStatus = myFtdiDevice.Write(dataBuffer, dataBuffer.Length, ref bytesWritten);
            if (ftStatus != FTDI.FT_STATUS.FT_OK)
            {
                WriteInfo("Не удалось записать данные на устройство! (Ошибка " + ftStatus.ToString() + ")");
                button2.Text = "Start";
                go = false;
                return (datagraf);
            }

            byte[] dataRead = new byte[frames * 2];
            double[] dataProcessed = new double[frames];
            //double[] dataTemp = new double[frames];
            uint bytesRead = 0;

            ftStatus = myFtdiDevice.Read(dataRead, (uint)(frames * 2), ref bytesRead);

            if (ftStatus != FTDI.FT_STATUS.FT_OK)
            {
                WriteInfo("Не удалось прочесть данные с устройства! (Ошибка " + ftStatus.ToString() + ")");
                button2.Text = "Start";
                go = false;
                return (datagraf);
            }
            for (int i = 0; i < (frames * 2); i += 2) dataProcessed[i / 2] = ((int)dataRead[i] + (int)dataRead[i + 1] * 256);
            try
            {
                if (decB) for (int i = 0; i < (frames); i++) dataProcessed[i] -= basespec[i];
            }
            catch
            { }
            datagraf = dataProcessed;
            return (datagraf);



        }

        int IntTime = 100;
        bool go = false; // съем данных
        double[] basespec;//спектр базы
        bool decB = false; //вычитание базы
        double[] datagraf = new double[frames];//обработанные данные с устройства
        private const int frames = 3600;
        FTDI myFtdiDevice;
        FTDI.FT_STATUS ftStatus;
        int FramesAfterParamsSet = 0;

        public void button3_Click(object sender, EventArgs e)
        {
            Start();
        }

        public void Start()
        {
            if (go)
                return;
            go = initialization();
            backgroundWorker1.RunWorkerAsync();
        }

        private void button4_Click(object sender, EventArgs e)
        {
            if (go)
            {
                go = false;
                closedevice();
            }
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            while (go)
            {
                datagraf = transferData();
                FramesAfterParamsSet++;
                (sender as BackgroundWorker).ReportProgress(0, datagraf);
                System.Threading.Thread.Sleep(100);
                System.GC.Collect();
            }
        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            tChart1.Series[0].Clear();

            int NN = CropTo-CropFrom+1;
            double []X = new double[NN];
            double []Y = new double[NN];

            if (Scale == null)
            {
                Scale = new double[frames];
                for (int i = 0; i < frames; i++)
                    Scale[i] = i;
            }

            Array.Copy((e.UserState as double[]), CropFrom - 1, Y, 0, NN);
            Array.Copy(Scale, CropFrom - 1, X, 0, NN);

            tChart1.Series[0].Add(X, Y);
            RefreshPanel();

            //do some math
            double[] s = Y;// e.UserState as double[];
            int N = Y.Length;
            double[] sum = new double[N];

            sum[0]=s[0];
            MaxX = 0;
            double MaxY = s[0], MinY = s[0];

            for (int i = 1; i < N; i++)
            {
                sum[i] = sum[i - 1] + s[i];
                if (s[i] > MaxY)
                {
                    MaxX = i;
                    MaxY = s[i];
                }
                if (s[i] < MinY)
                    MinY = s[i];
            }

            

            //calc half height width in Max
            int l = 0, r = 0;
            double HH = MaxY/2;
            for (int i = MaxX; i >= 0 && s[i] > HH; i--)
                l++;
            for (int i = MaxX; i < N && s[i] > HH; i++)
                r++;

            int WHH = r + l - 2;

            int M1 = M[0] - CropFrom + 1; M1 = M1 > 0 ? M1 : 0; M1 = M1 < N ? M1 : N - 1;
            int M2 = M[1] - CropFrom + 1; M2 = M2 > 0 ? M2 : 0; M2 = M2 < N ? M2 : N - 1;
            int M3 = M[2] - CropFrom + 1; M3 = M3 > 0 ? M3 : 0; M3 = M3 < N ? M3 : N - 1;
            int M4 = M[3] - CropFrom + 1; M4 = M4 > 0 ? M4 : 0; M4 = M4 < N ? M4 : N - 1;

            double LESA = double.NaN;


                if (M1 != M2)
                    LESA = ((sum[M4] - sum[M3]) - (s[M4] - s[M3]) / 2 * (M4 - M3)) /
                           ((sum[M2] - sum[M1]) - (s[M2] - s[M1]) / 2 * (M2 - M1));



            double[] ax = new double[] {0, M1, M1, M2, M2, M3, M3, M4, M4, N-1};
            if (Scale != null)
                ax = new double[] { X[0], X[M1], X[M1], X[M2], X[M2], X[M3], X[M3], X[M4], X[M4], X[X.Length-1]};
            double [] ay = new double[]{MinY, MinY, MaxY, MaxY,MinY, MinY, MaxY, MaxY,MinY, MinY};

            tChart1.Series[1].Clear();
            tChart1.Series[1].Add(ax, ay);

            //int time adjustment
            int NewIntTime = IntTime;
            if (FramesAfterParamsSet > 1)
            {
                if (MaxY > 4090)
                    NewIntTime /= 2;
                if (MaxY < 1000)
                    NewIntTime *= (int)(3000.0 / MaxY);
                if (NewIntTime < 5)
                    NewIntTime = 5;
                if (NewIntTime > 200)
                    NewIntTime = 200;

                if (IntTime != NewIntTime)
                {
                    IntTime = NewIntTime;
                    SetDeviceParams();
                }
            }

            LesaText = String.Format("LESA = {0}, Int.time = {1}, WHH = {2}", Math.Round(LESA, 2), IntTime, WHH);
            tChart1.Header.Text = String.Format("LESA = {0}, Int.time = {1}, WHH = {2}", Math.Round(LESA, 2), IntTime, WHH);

            //Update other windows

            
        }

        public string LesaText = "";

        private void tChart1_Click(object sender, EventArgs e)
        {

        }

        private void tChart1_Click_1(object sender, EventArgs e)
        {

        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {

            if (e.CloseReason == CloseReason.UserClosing)
            {
                e.Cancel = true;
                Hide();
                return;
            }
            if (go)
            {
                go = false;
                closedevice();
                e.Cancel = true;
            }
        }

        int MaxX = 0;
        int LaserPixelNumber = 0;
        double LaserWL = 0;
        double[] Scale = null;
        public int[] M = new int[4];
        public int CropFrom = 1, CropTo = frames - 1;
        public double A = 1, B = 0;
        
        private void button6_Click(object sender, EventArgs e)
        {
            if (MaxX == 0)
                return;
            Scale = new double[frames];
            LaserWL = (double)PeakWL.Value;
            LaserPixelNumber = MaxX;
            A = (double)Dispersion.Value;
            B = LaserWL - A * (double)LaserPixelNumber;


            for (int i = 0; i < frames; i++)
                Scale[i] = A * i + B;

            RefreshMarkers();
        }

        private void numericUpDown1_ValueChanged(object sender, EventArgs e)
        {

            RefreshMarkers();

        }

        private void RefreshMarkers()
        {
            M[0] = (int)numericUpDown1.Value;
            M[1] = (int)numericUpDown2.Value;
            M[2] = (int)numericUpDown3.Value;
            M[3] = (int)numericUpDown4.Value;

            if (Scale == null)
                return;

            label1.Text = Scale[M[0]].ToString() + " nm";
            label2.Text = Scale[M[1]].ToString() + " nm";
            label3.Text = Scale[M[2]].ToString() + " nm";
            label4.Text = Scale[M[3]].ToString() + " nm";
        }

        private void TrimFrom_ValueChanged(object sender, EventArgs e)
        {
            CropFrom = (int)TrimFrom.Value;

        }

        private void TrimTo_ValueChanged(object sender, EventArgs e)
        {
            CropTo = (int)TrimTo.Value;
        }

    }
}
